package com.qunchuang.netty;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.digitalpetri.netty.fsm.Event;
import com.qunchuang.constant.LiftConstant;
import com.qunchuang.constant.LiftTaskStateConstant;
import com.qunchuang.mapper.LiftAgvcTaskMapper;
import com.qunchuang.mapper.LiftMapper;
import com.qunchuang.pojo.entity.LiftAgvcHistoryEntity;
import com.qunchuang.pojo.entity.LiftAgvcTaskEntity;
import com.qunchuang.pojo.entity.LiftEntity;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.util.CharsetUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import static com.qunchuang.netty.Client.CHANNEL;
import static com.qunchuang.timers.LiftTaskStateTimersTcp.LIFT_STATE_MAP;
import static com.qunchuang.timers.LiftTaskStateTimersTcp.calculateChecksum;

/**
 * 处理类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class Handler extends ChannelInboundHandlerAdapter {
    //    @Autowired
    private final LiftAgvcTaskMapper liftAgvcTaskMapper;
    //    @Autowired
    private final LiftMapper liftMapper;
//    private final Connect connect;

    private final ReconnectClient reconnectClient;

//    private final Client client;
    /**
     * 管理一个全局map，保存连接进服务端的通道数量
     */
    public static final ConcurrentHashMap<ChannelId, ChannelHandlerContext> CHANNEL_MAP = new ConcurrentHashMap<>();
    public static final ConcurrentHashMap<String, String> LIFT_SERVE_STATE_MAP = new ConcurrentHashMap<>();

    /**
     * 从服务端收到新的数据时，这个方法会在收到消息时被调用
     * 这里写收到服务端的数据之后要做的处理，通常有数据类型转换，数据解析
     *
     * @param ctx
     * @param msg
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception, IOException {
        log.info("msg返回值：{}", msg);
        ByteBuf buf = (ByteBuf) msg;
        log.info("可读长度：{}", buf.readableBytes());
        String stringMsg = ByteBufUtil.hexDump(buf);
        log.info("16进制返回值：{}", stringMsg);
        //根据协议看是否需要对msg数据进行类型转换，解析
        Channel channel = ctx.channel();
//        ChannelId ctxId = ctx.channel().id();
//                String msgString = msg.toString();
        int length = stringMsg.length();
        //根据应答命令来判断
        if (length == 12 || length == 20) {
            String substring = stringMsg.substring(4, 6);
            String response = stringMsg.substring(6, 8);
//                    String checkSum = stringMsg.substring(msgString.length() - 2);
            switch (substring) {
                case "a1":
                    if (response.equals("00")) {
                        log.info("执行呼梯命令");
                        for (String liftId : CHANNEL.keySet()) {
                            ChannelFuture channelFuture = CHANNEL.get(liftId);
//                            ChannelId channelId = channelFuture.channel().id();
                            Channel channel1 = channelFuture.channel();
                            if (channel == channel1) {
                                LIFT_STATE_MAP.put(LiftTaskStateConstant.CALL_LIFT_COMMAND, liftId);
                                log.info("传入map的参数为：{}", LIFT_STATE_MAP);
//                                        }
                            }
                        }
                    } /*else {
                                LIFT_STATE_MAP.remove(LiftTaskStateConstant.CALL_LIFT_COMMAND);
                            }*/
                    break;
                case "a2":
                    if (response.equals("00")) {
                        log.info("执行关门到某一层");
                        for (String liftId : CHANNEL.keySet()) {
                            ChannelFuture channelFuture = CHANNEL.get(liftId);
//                            ChannelId channelId = channelFuture.channel().id();
                            Channel channel1 = channelFuture.channel();
                            if (channel == channel1) {
//                                        LIFT_SERVE_STATE_MAP.put(LiftTaskStateConstant.LIFT_CLOSE_FINAL_FLOOR,1);
                                LIFT_SERVE_STATE_MAP.put(LiftTaskStateConstant.LIFT_CLOSE_FINAL_FLOOR, liftId);
                                break;
                            }
                        }
                    }
                    break;
                case "a5":
                    if (response.equals("00")) {
                        //关门
                        log.info("执行关门命令");
                        for (String liftId : CHANNEL.keySet()) {
                            ChannelFuture channelFuture = CHANNEL.get(liftId);
//                            ChannelId channelId = channelFuture.channel().id();
                            Channel channel1 = channelFuture.channel();
                            if (channel == channel1) {
//                                        LIFT_SERVE_STATE_MAP.put(LiftTaskStateConstant.LIFT_CLOSE_DOOR,1);
                                LIFT_SERVE_STATE_MAP.put(LiftTaskStateConstant.LIFT_CLOSE_DOOR, liftId);
                                break;
                            }
                        }
                    }
                    break;
                case "c0":
                    log.info("查询电梯状态");
                    if (response.equals("00")) {
                        String replyStateOne = stringMsg.substring(8, 10);
                        String replyStateTwo = stringMsg.substring(10, 12);
                        String replyStateThree = stringMsg.substring(12, 14);
                        String replyStateFour = stringMsg.substring(14, 16);
                        String hexToBinaryFour = hexToBinary(replyStateFour);
                        //判断电梯状态2是否专用
                        Integer liftStateTwo = Integer.valueOf(replyStateTwo);
                        log.info("电梯处于agv专用");
                        for (String liftId : CHANNEL.keySet()) {
                            ChannelFuture channelFuture = CHANNEL.get(liftId);
                            Channel channel1 = channelFuture.channel();
//                            ChannelId channelId = channelFuture.channel().id();
                            if (channel == channel1) {
                                //修改电梯楼层
                                LiftEntity liftEntity = new LiftEntity();
                                liftEntity.setLiftRemainFloor(Integer.parseInt(replyStateOne));
                                log.info("电梯停留楼层{}", Integer.parseInt(replyStateOne));
//                                            String liftServeState = hexToBinaryFour.substring( 1,2);
                                String liftServeState = hexToBinaryFour.substring(6, 7);
                                Integer liftState = Integer.valueOf(liftServeState);
                                log.info("查询向SELECT_LIFT_SERVE_STATE添加为：{}", liftState);
                                LIFT_SERVE_STATE_MAP.put(LiftTaskStateConstant.SELECT_LIFT_SERVE_STATE, liftState + liftId);
                                //电梯门是否关门到位
//                                            String liftDoorClose = hexToBinaryFour.substring(7);
                                String liftDoorClose = hexToBinaryFour.substring(0, 1);
                                Integer i = Integer.valueOf(liftDoorClose);
//                                            LIFT_SERVE_STATE_MAP.put(LiftTaskStateConstant.SELECT_LIFT_DOOR_STATE,i);
                                //电梯门是否关门到位
//                                            String liftDoorOpen = hexToBinaryFour.substring(3, 4);
                                String liftDoorOpen = hexToBinaryFour.substring(4, 5);
                                if (liftDoorOpen.equals("1")) {
                                    liftEntity.setLiftDoor(LiftConstant.LIFT_DOOR_OPEN);
                                }
                                if (liftDoorClose.equals("1")) {
                                    liftEntity.setLiftDoor(LiftConstant.LIFT_DOOR_CLOSE);
                                }
                                QueryWrapper<LiftEntity> liftEntityQueryWrapper = new QueryWrapper<>();
                                log.info("打印日志：{}", liftId);
                                log.info("打印电梯日志：{}", liftEntity);
                                liftEntityQueryWrapper.eq("lift_id", liftId);
                                liftMapper.update(liftEntity, liftEntityQueryWrapper);
                            }
                        }
                    }
                    break;
                default:
                    break;
            }
//            Thread.sleep(10000);
//            ByteBuf buffer = Unpooled.buffer(10);
//            buffer.writeByte(0xaa);
//            buffer.writeByte(0x09);
//            buffer.writeByte(0x00);
//            buffer.writeByte(0x00);
//            buffer.writeByte(0x40);
//            buffer.writeByte(0x00);
//            buffer.writeByte(0x00);
//            buffer.writeByte(0x00);
//            buffer.writeByte(0x49);
//            buffer.writeByte(0xbb);
//            String s = ByteBufUtil.hexDump(buffer);
//            log.info("电梯回复后，查询电梯状态：{}",s);
//            ctx.writeAndFlush(buffer);
        }
        ctx.flush();
    }

    private static ResultS getResultS(String msgString) {
        String substring1 = msgString.substring(0, 2);
        String substring2 = msgString.substring(2, 4);
        String substring3 = msgString.substring(4, 6);
        String substring5 = msgString.substring(8, 10);
        String substring6 = msgString.substring(10, 12);
        String substring7 = msgString.substring(12, 14);
        String substring8 = msgString.substring(14, 16);
        String substring9 = msgString.substring(16, 18);
        byte b0 = Byte.parseByte(substring1, 16);
        byte b1 = Byte.parseByte(substring2, 16);
        byte b2 = Byte.parseByte(substring3, 16);
        byte b4 = Byte.parseByte(substring5, 16);
        byte b5 = Byte.parseByte(substring6, 16);
        byte b6 = Byte.parseByte(substring7, 16);
        byte b7 = Byte.parseByte(substring8, 16);
        byte b8 = Byte.parseByte(substring9, 16);
        ResultS result = new ResultS(b0, b1, b2, b4, b5, b6, b7, b8);
        return result;
    }

    private static class ResultS {
        public final byte b0;
        public final byte b1;
        public final byte b2;
        public final byte b4;
        public final byte b5;
        public final byte b6;
        public final byte b7;
        public final byte b8;

        public ResultS(byte b0, byte b1, byte b2, byte b4, byte b5, byte b6, byte b7, byte b8) {
            this.b0 = b0;
            this.b1 = b1;
            this.b2 = b2;
            this.b4 = b4;
            this.b5 = b5;
            this.b6 = b6;
            this.b7 = b7;
            this.b8 = b8;
        }
    }

    private static Result getResult(String msgString) {
        String substring2 = msgString.substring(0, 2);
        String substring3 = msgString.substring(2, 4);
        String substring4 = msgString.substring(4, 6);
        String substring6 = msgString.substring(8, 10);
        byte b0 = Byte.parseByte(substring2, 16);
        byte b1 = Byte.parseByte(substring3, 16);
        byte b2 = Byte.parseByte(substring4, 16);
        byte b4 = Byte.parseByte(substring6, 16);
        Result result = new Result(b0, b1, b2, b4);
        return result;
    }

    private static class Result {
        public final byte b0;
        public final byte b1;
        public final byte b2;
        public final byte b4;

        public Result(byte b0, byte b1, byte b2, byte b4) {
            this.b0 = b0;
            this.b1 = b1;
            this.b2 = b2;
            this.b4 = b4;
        }
    }

    private void updateLiftTaskState(Integer id, Integer state) {
        LiftAgvcTaskEntity liftAgvTaskEntity = new LiftAgvcTaskEntity();
        liftAgvTaskEntity.setState(state);
        QueryWrapper<LiftAgvcTaskEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        liftAgvcTaskMapper.update(liftAgvTaskEntity, queryWrapper);
    }

    private String hexToBinary(String hexByte) {
        int decimalByte = Integer.parseInt(hexByte, 16); // 将十六进制转换为整数
        String binaryByte = Integer.toBinaryString(decimalByte); // 转换为二进制字符串

        // 补齐到8位，如果不足8位则在前面补0
        while (binaryByte.length() < 8) {
            binaryByte = "0" + binaryByte;
        }

        return binaryByte;
    }

    /**
     * 从服务端收到新的数据、读取完成时调用
     *
     * @param ctx
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
        //回应服务端
        ctx.write("接收到了新数据----------------------!");
        ctx.flush();
    }

    /**
     * 当出现 Throwable 对象才会被调用，即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     *
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException {
        System.out.println("exceptionCaught");
        cause.printStackTrace();
        ctx.close();//抛出异常，断开与客户端的连接
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelActive(ctx);
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = insocket.getAddress().getHostAddress();
        System.out.println("channelActive:" + clientIp + ctx.name());
        ByteBuf message = null;
        byte[] req = ("I am client once").getBytes();
//        for(int i = 0; i < 5; i++) {
//            message = Unpooled.buffer(req.length);
//            message.writeBytes(req);
//            Thread.sleep(5000);
////            ctx.channel()
//            ctx.writeAndFlush(message);
//        }

    }

    /**
     * 客户端与服务端 断连时 执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(final ChannelHandlerContext ctx) {
        log.info("连接断开：{}",ctx.channel().remoteAddress());
//        super.channelInactive(ctx);
//        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
//        String clientIp = insocket.getAddress().getHostAddress();
//        ctx.close(); //断开连接时，必须关闭，否则造成资源浪费
//        System.out.println("channelInactive:" + clientIp);
//        // 可以在这里添加重连逻辑
//        ctx.channel().eventLoop().schedule(() -> {0.
//            try {
//                // 尝试重新连接
//                List<LiftEntity> liftEntities = liftMapper.selectList(null);
//                log.info("重连后查询电梯表结果：{}",liftEntities);
//                for (LiftEntity liftEntity : liftEntities) {
//                    String ip = liftEntity.getLiftIp();
//                    Integer port = liftEntity.getLiftPort();
//                    St
//                    ring liftId = liftEntity.getLiftId();
//                    log.info("电梯ip为：{},电梯端口号为：{},电梯id为：{}",ip,port,liftId);
//                    client.connect(port,ip,liftId);//TCP工具做服务端进行测试的时候的端口和ip
////            connect(,ip,liftId);//TCP工具做服务端进行测试的时候的端口和ip
//                }
//            } catch (Exception e) {
//                log.error("重连失败", e);
//            }
//        }, 3000, TimeUnit.MILLISECONDS);
    }

    @Override
    public void channelUnregistered(final ChannelHandlerContext ctx) throws Exception {

        ctx.channel().eventLoop().schedule(() -> {
            try {
                // 尝试重新连接
                List<LiftEntity> liftEntities = liftMapper.selectList(null);
                log.info("重连后查询电梯表结果：{}",liftEntities);
                for (LiftEntity liftEntity : liftEntities) {
                    String ip = liftEntity.getLiftIp();
                    Integer port = liftEntity.getLiftPort();
                    String liftId = liftEntity.getLiftId();
                    log.info("电梯ip为：{},电梯端口号为：{},电梯id为：{}",ip,port,liftId);
                    reconnectClient.connect(port,ip,liftId);//TCP工具做服务端进行测试的时候的端口和ip
//            connect(,ip,liftId);//TCP工具做服务端进行测试的时候的端口和ip
                }
            } catch (Exception e) {
                log.error("重连失败", e);
            }
        }, 3000, TimeUnit.MILLISECONDS);
    }


    /**
     * 广播数据十六进制,每两个字符为一组，
     * 如：09 57 79 30 30 30 30 30 30 30 30 30 30 30 41 30 30 35 30 30
     * 再转为对应ASCII码,即可得到明文的uuid
     *
     * @param uuid
     */
    public String hexToASCII(String uuid) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < uuid.length(); i += 2) {
            String hex = uuid.substring(i, i + 2);
            int decimal = Integer.parseInt(hex, 16);
            char asciiChar = (char) decimal;
            sb.append(asciiChar);
        }
        String result = sb.toString();
        return result;
    }

    /**
     * 将时间戳转换为yyyy-MM-dd HH:mm:ss格式的日期字符串。
     *
     * @param timestamp 时间戳（以毫秒为单位）
     * @return 格式化后的日期字符串
     */
    public String timestampToDate(long timestamp) {
        Instant instant = Instant.ofEpochSecond(timestamp); // 将时间戳转换为Instant对象
        LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); // 将Instant对象转换为本地日期时间对象
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 定义日期格式
        // 将日期时间对象格式化为字符串
        String time = dateTime.format(formatter);
        return time;
    }

    /**
     * 将LocalDateTime转换为yyyy-MM-dd HH:mm:ss格式的日期字符串。
     *
     * @return 格式化后的日期字符串
     */
    public String LocalDateTimeToStr() {
        // 获取当前日期时间
        LocalDateTime now = LocalDateTime.now();
        // 定义日期时间的格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 将LocalDateTime格式化为字符串
        String formattedDateTime = now.format(formatter);
        return formattedDateTime;
    }


    /**
     * @param msg       需要发送的消息内容
     * @param channelId 连接通道唯一id
     * @author xiongchuan on 2019/4/28 16:10
     * @DESCRIPTION: 服务端给客户端发送消息
     * @return: void
     */
    public void channelWrite(ChannelId channelId, Object msg) throws Exception {

        ChannelHandlerContext ctx = CHANNEL_MAP.get(channelId);

        if (ctx == null) {
            System.out.println("通道【" + channelId + "】不存在");
            return;
        }

        if (msg == null || msg == "") {
            System.out.println("服务端响应空的消息");
            return;
        }

        //将客户端的信息直接返回写入ctx
        ctx.write(msg);
        //刷新缓存区
        ctx.flush();
    }
}
